업무 모듈 디렉토리 깊게 파헤치기

Areas·Controllers·Pages·Contracts의 역할을 구분하고, 실행 중인 모듈 설정을 찾습니다.

QCN

ASP.NET Core 공유 프레임워크 사용 (1/2)

  • .NET Core 3.0 이후 클래스 라이브러리에서 ASP.NET Core API 사용하도록 개선되었습니다.

  • Microsoft.NET.Sdk 또는 Microsoft.NET.Sdk.Razor SDK를 사용하는 프로젝트는 공유 프레임워크에서 ASP.NET Core API를 사용하려면 ASP.NET Core를 참조합니다.

  • MVC 확장성 포함 Razor 보기 또는 Razor 페이지 지원이 가능합니다.

QCN

ASP.NET Core 공유 프레임워크 사용 (2/2)

<Project Sdk="Microsoft.NET.Sdk.Razor">
  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
  </ItemGroup>
</Project>
QCN

Area란 무엇인가? (1/2)

  • ASP.NET Core 에서 컨트롤러, 뷰, 모델을 논리적으로 구분해 영역별로 모듈화하기 위한 기능입니다.

  • Area를 경로에 포함해 모듈을 구분합니다. 실제 Route 조합의 충돌 여부는 확인해야 합니다.

/{area}/{controller}/{action}/{id?}
QCN

Area란 무엇인가? (2/2)

/Areas
   /Admin
      /Controllers
         DashboardController.cs
      /Views
         /Dashboard
            Index.cshtml
      /Models
QCN

API의 심장: Controllers 디렉토리 (1/2)

  • 위치: Areas/[모듈 ID]/Controllers

  • 역할: 클라이언트(웹 브라우저 등)로부터 오는 API 요청을 받아 처리하는 로직이 담겨있습니다.

  • 파일: HomeController.cs, BoardController.cs 와 같은 C# 클래스 파일로 구성됩니다.

  • 주로 JSON 형태의 데이터를 주고받는 RESTful API 엔드포인트를 정의합니다.

QCN

API의 심장: Controllers 디렉토리 (2/2)

/Areas
└── sample
    └── Controllers
        └── HomeController.cs
QCN

Controllers 예시 코드 (1/3)

  • /[모듈 ID]/api/home/hello 요청을 처리하는 간단한 예시입니다.

  • 다음 코드는 GET /sample/api/home/hello 요청 시 "Hello from Server!" 라는 텍스트를 반환합니다.

QCN

Controllers 예시 코드 (2/3)

using Microsoft.AspNetCore.Mvc;

namespace sample.Areas.sample.Controllers
{
    [Area("sample")]
    [Route("[area]/api/[controller]")]
    public class HomeController : Controller
    {
        [HttpGet("hello")]
        public ActionResult<string> SayHello()
        {
            return "Hello from Server!";
        }
    }
QCN

Controllers 예시 코드 (3/3)

}
QCN

서버가 그리는 화면: Pages 디렉토리 (1/2)

  • 위치: Areas/[모듈 ID]/Pages

  • 역할: 서버 측에서 렌더링되는 UI, 즉 사용자에게 보여질 화면(HTML)을 만듭니다.

  • 파일: 주로 Razor Pages 기술을 사용하며, Index.cshtml(View)과 Index.cshtml.cs(Code-behind) 파일이 하나의 쌍으로 동작합니다.

  • 서버의 데이터를 받아 동적인 웹 페이지를 생성할 때 사용됩니다.

QCN

서버가 그리는 화면: Pages 디렉토리 (2/2)

/Areas
└── Sample
    └── Pages
        ├── Index.cshtml
        └── Index.cshtml.cs
QCN

Pages 파일의 역할 (1/2)

  • Index.cshtml

    • HTML 구조와 함께 C# 코드를 사용하여 서버 데이터를 화면에 표시합니다.
    • @ 기호를 사용하여 C# 변수나 로직을 HTML에 삽입합니다.
  • Index.cshtml.cs (코드 비하인드)

    • 페이지가 로드될 때 실행될 C# 로직을 담습니다.
    • 데이터베이스에서 데이터를 조회하거나, 클라이언트 요청을 처리하여 그 결과를 cshtml 파일로 전달합니다.
QCN

Pages 파일의 역할 (2/2)

QCN

계약의 정의: Contracts 디렉토리 (1/2)

  • 위치: [모듈 ID]/Contracts 화면/기능 개발자의 메인 디렉토리입니다.

  • 컴파일 할 때 모든 모듈의 환경 설정과 개발 소스는 프론트엔드와 백엔드 모두 $(HANDSTACK_HOME)/contracts 단일 디렉토리내에서 관리됩니다.

  • 화면·쿼리 계약은 텍스트로 편집합니다. 반영 경로와 캐시 정책을 확인하며, 서버 C# 변경은 별도 빌드가 필요합니다.

QCN

계약의 정의: Contracts 디렉토리 (2/2)

[모듈 ID]/contracts
├─command
├─dbclient
├─function
├─graphclient
├─prompter
├─repository
├─transact
└─wwwroot
QCN

contracts 디렉토리 구조

프로젝트 관리에 필요한 소스 운영 기준을 만들기 위해 디렉토리와 파일 명명에 대한 규칙을 다음과 같이 권장합니다.

module
   └─HDS: 기본 애플리케이션 ID 3자리
       └─TST: 프로젝트 ID 3자리
           └─TST010: 파일 ID
QCN

어플리케이션 계약 디렉토리 (1/4)

HandStack에서 공식 제공되는 모든 디렉토리와 소스 코드 들은 이 규칙을 준수합니다.

QCN

어플리케이션 계약 디렉토리 (2/4)

%HANDSTACK_HOME%\contracts
├─command
│  └─HDS
│      └─TST
├─dbclient
│  └─HDS
│      └─TST
├─function
│  ├─csharp
│  │  └─HDS
│  │      └─TST
│  │          └─TST010
│  └─javascript
│      └─HDS
QCN

어플리케이션 계약 디렉토리 (3/4)

│          └─TST
│              └─TST020
├─graphclient
│  └─HDS
│      └─TST
├─prompter
│  └─HDS
│      └─TST
├─repository
│  └─HDS
├─transact
│  └─HDS
│      ├─STR
│      └─TST
QCN

어플리케이션 계약 디렉토리 (4/4)

└─wwwroot
   └─HDS
       └─TST
QCN

그외에 모듈 내 디렉토리 구조

  • 모듈의 가독성과 유지보수성을 위해 다음과 같은 디렉토리 구조를 권장합니다.

  • Entity: 데이터베이스 테이블과 매핑되는 모델 클래스

  • Events: 비동기 처리를 위한 이벤트 정의

  • Extensions: 기존 클래스를 확장하는 헬퍼 메서드

  • Services: 비즈니스 로직을 처리하는 서비스 클래스

  • Settings: 모듈별 환경 설정 값

QCN

HandStack의 ack 서버

  • ack 서버는 HandStack 애플리케이션의 시작점입니다.
  • 이 웹 서버가 시작될 때, 필요한 모듈들을 로딩하여 메모리에 올립니다.
  • 그 후, 클라이언트로부터 들어오는 모든 HTTP 요청(API, Page)을 각 모듈의 적절한 Controller나 Page로 전달하고 응답을 처리하는 중심 역할을 수행합니다.
  • ack 서버의 appsettings.json 에 로드되는 모듈 정보를 확인하세요.
QCN

잠깐, 구분해 보기

소스의 module.json을 바꿨는데 동작이 그대로인 이유는 무엇일까요?

QCN

로딩되는 모듈 확인하기 (1/3)

  • ack 서버가 어떤 모듈을 로드하는지는 설정 파일에서 확인할 수 있습니다.

  • 아래 경로의 파일을 열어 LoadModules 항목을 살펴봅시다.

    • 경로: $(HANDSTACK_SRC)/1.WebHost/ack/appsettings.json
QCN

로딩되는 모듈 확인하기 (2/3)

{
  // ... 생략 ...
  "LoadModules": [
      "wwwroot",
      "transact",
      "dbclient",
      "graphclient",
      "function",
      "command",
      "prompter",
      "repository",
      "logger",
      "checkup",
      "forwarder"
QCN

로딩되는 모듈 확인하기 (3/3)

  ],
  // ... 생략 ...
}
QCN

checkup 모듈 설정 확인하기 (1/3)

QCN

checkup 모듈 설정 확인하기 (2/3)

{
    "ModuleID": "checkup",
    "Name": "checkup",
    "IsBundledWithHost": false,
    "Version": "1.0.0",
    "ModuleConfig": {
        "SystemID": "HANDSTACK",
        "ManagedAccessKey": "6eac215f2f5e495cad4f2abfdcad7644",
        "EncryptionAES256Key": "1234567890123456",
        "AdministratorEmailID": "administrator@handstack.kr",
        "ModuleConfigurationUrl": "http://localhost:8421/checkup/api/managed/initialize-settings",
        "BusinessServerUrl": "http://localhost:8421/transact/api/transaction/execute",
        "ModuleLogFilePath": "../log/checkup/module.log",
QCN

checkup 모듈 설정 확인하기 (3/3)

        "ModuleBasePath": "../modules/checkup",
        "WWWRootBasePath": "../modules/checkup/wwwroot/checkup",
        "EventAction": [
            "repository.Events.RepositoryRequest"
        ],
        "SubscribeAction": [],
        "ConnectionString": "URI=file:../sqlite/HDS/dbclient/checkup.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;"
    }
}
QCN

dbclient 모듈 설정 확인하기 (1/8)

dbclient 모듈 참고하기

QCN

dbclient 모듈 설정 확인하기 (2/8)

{
    "ModuleID": "dbclient",
    "Name": "dbclient",
    "IsBundledWithHost": false,
    "Version": "1.0.0",
    "ModuleConfig": {
        "SystemID": "HANDSTACK",
        "BusinessServerUrl": "http://localhost:8421/transact/api/transaction/execute",
        "CircuitBreakResetSecond": 60,
        "DefaultCommandTimeout": 30,
        "ContractBasePath": [
            "../contracts/dbclient"
        ],
        "IsTransactionLogging": false,
QCN

dbclient 모듈 설정 확인하기 (3/8)

        "ModuleLogFilePath": "../log/dbclient/module.log",
        "IsLogServer": true,
        "LogServerUrl": "http://localhost:8421/logger/api/log/insert",
        "IsProfileLogging": false,
        "ProfileLogFilePath": "../log/dbclient/profile.log",
        "EventAction": [],
        "SubscribeAction": [
            "dbclient.Events.DbClientRequest",
            "dbclient.Events.ManagedRequest"
        ],
        "DataSource": [
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
QCN

dbclient 모듈 설정 확인하기 (4/8)

                "DataSourceID": "CHECKUPDB",
                "DataProvider": "SQLite",
                "ConnectionString": "URI=file:../sqlite/HDS/dbclient/checkup.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;",
                "IsEncryption": "N",
                "Comment": "SQLite 기본 데이터베이스"
            },
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "DB01",
                "DataProvider": "SQLite",
                "ConnectionString": "URI=file:../sqlite/HDS/dbclient/HDS.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;",
QCN

dbclient 모듈 설정 확인하기 (5/8)

                "IsEncryption": "N",
                "Comment": "SQLite 기본 데이터베이스"
            },
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "DB02",
                "DataProvider": "SqlServer",
                "ConnectionString": "Data Source=localhost;Initial Catalog=master;User ID=sa;Password=Strong@Passw0rd;",
                "IsEncryption": "N",
                "Comment": "SqlServer 기본 데이터베이스"
            },
            {
QCN

dbclient 모듈 설정 확인하기 (6/8)

                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "DB03",
                "DataProvider": "Oracle",
                "ConnectionString": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SID=ORCL)));User Id=system;Password=Strong@Passw0rd;",
                "IsEncryption": "N",
                "Comment": "Oracle 기본 데이터베이스"
            },
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "DB04",
                "DataProvider": "MySQL",
QCN

dbclient 모듈 설정 확인하기 (7/8)

                "ConnectionString": "Server=localhost;Port=3306;Uid=root;Pwd=Strong@Passw0rd;PersistSecurityInfo=True;SslMode=none;Charset=utf8;Allow User Variables=True;",
                "IsEncryption": "N",
                "Comment": "MySQL 기본 데이터베이스"
            },
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "DB05",
                "DataProvider": "PostgreSQL",
                "ConnectionString": "Host=localhost;Port=5432;Database=postgres;User ID=postgres;Password=Strong@Passw0rd;",
                "IsEncryption": "N",
                "Comment": "PostgreSQL 기본 데이터베이스"
QCN

dbclient 모듈 설정 확인하기 (8/8)

            }
        ]
    }
}
QCN

function 모듈 설정 확인하기 (1/8)

nuget, npm, pypi 패키지 관리자로 서버 함수에 필요한 패키지를 관리합니다.

function 모듈 참고하기

QCN

function 모듈 설정 확인하기 (2/8)

{
    "ModuleID": "function",
    "Name": "function",
    "IsBundledWithHost": false,
    "Version": "1.0.0",
    "ModuleConfig": {
        "SystemID": "HANDSTACK",
        "BusinessServerUrl": "http://localhost:8421/transact/api/transaction/execute",
        "CircuitBreakResetSecond": 60,
        "IsLogServer": true,
        "LogServerUrl": "http://localhost:8421/logger/api/log/insert",
        "ContractBasePath": [
            "../contracts/function"
        ],
QCN

function 모듈 설정 확인하기 (3/8)

        "ModuleLogFilePath": "../log/function/module.log",
        "NodeFunctionConfig": {
            "LocalStoragePath": "../cache/function",
            "LogMinimumLevel": "trace",
            "FileLogBasePath": "../log/function/javascript",
            "TimeoutMS": -1,
            "IsSingleThread": true,
            "WatchGracefulShutdown": true,
            "EnableFileWatching": true,
            "WatchFileNamePatterns": [ "featureMain.js" ],
            "NodeAndV8Options": "",
            "EnvironmentVariables": ""
        },
        "CSharpFunctionConfig": {
QCN

function 모듈 설정 확인하기 (4/8)

            "EnableFileWatching": true,
            "FileLogBasePath": "../log/function/csharp",
            "WatchFileNamePatterns": [ "featureMain.cs" ],
        },
        "EventAction": [],
        "SubscribeAction": [],
        "FunctionSource": [
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "FN01",
                "DataProvider": "SQLite",
                "ConnectionString": "URI=file:../sqlite/HDS/dbclient/HDS.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;",
QCN

function 모듈 설정 확인하기 (5/8)

                "IsEncryption": "N",
                "WorkingDirectoryPath": "../tmp/HDS/function/HDS_FN01",
                "Comment": "SQLite 기본 거래"
            },
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "FN02",
                "DataProvider": "SqlServer",
                "ConnectionString": "Data Source=localhost;Initial Catalog=master;User ID=sa;Password=Strong@Passw0rd;",
                "IsEncryption": "N",
                "WorkingDirectoryPath": "../tmp/HDS/function/HDS_FN02",
                "Comment": "SqlServer 기본 거래"
QCN

function 모듈 설정 확인하기 (6/8)

            },
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "FN03",
                "DataProvider": "Oracle",
                "ConnectionString": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SID=ORCL)));User Id=system;Password=Strong@Passw0rd;",
                "IsEncryption": "N",
                "WorkingDirectoryPath": "../tmp/HDS/function/HDS_FN03",
                "Comment": "Oracle 기본 거래"
            },
            {
                "ApplicationID": "HDS",
QCN

function 모듈 설정 확인하기 (7/8)

                "ProjectID": "*",
                "DataSourceID": "FN04",
                "DataProvider": "MySQL",
                "ConnectionString": "Server=localhost;Port=3306;Uid=root;Pwd=Strong@Passw0rd;PersistSecurityInfo=True;SslMode=none;Charset=utf8;Allow User Variables=True;",
                "IsEncryption": "N",
                "WorkingDirectoryPath": "../tmp/HDS/function/HDS_FN04",
                "Comment": "MySQL 기본 거래"
            },
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "DataSourceID": "FN05",
                "DataProvider": "PostgreSQL",
QCN

function 모듈 설정 확인하기 (8/8)

                "ConnectionString": "Host=localhost;Port=5432;Database=postgres;User ID=postgres;Password=Strong@Passw0rd;",
                "IsEncryption": "N",
                "WorkingDirectoryPath": "../tmp/HDS/function/HDS_FN05",
                "Comment": "PostgreSQL 기본 거래"
            }
        ]
    }
}
QCN

repository 모듈 설정 확인하기 (1/3)

repository 모듈 참고하기

QCN

repository 모듈 설정 확인하기 (2/3)

{
    "ModuleID": "repository",
    "Name": "repository",
    "IsBundledWithHost": false,
    "Version": "1.0.0",
    "ModuleConfig": {
        "SystemID": "HANDSTACK",
        "FileServerUrl": "http://localhost:8421",
        "BusinessServerUrl": "http://localhost:8421/transact/api/transaction/execute",
        "ContractBasePath": [
            "../contracts/repository"
        ],
        "ModuleLogFilePath": "../log/repository/module.log",
        "DatabaseContractPath": "../contracts/dbclient",
QCN

repository 모듈 설정 확인하기 (3/3)

        "ModuleBasePath": "../modules/repository",
        "EventAction": [],
        "SubscribeAction": [ "repository.Events.RepositoryRequest" ],
        "XFrameOptions": "ALLOW-FROM http://127.0.0.1:8421,http://localhost:8421",
        "ContentSecurityPolicy": "frame-ancestors 'self' http://127.0.0.1:8421 http://localhost:8421;"
    }
}
QCN

transact 모듈 설정 확인하기 (1/6)

transact 모듈 참고하기

QCN

transact 모듈 설정 확인하기 (2/6)

{
    "ModuleID": "transact",
    "Name": "transact",
    "IsBundledWithHost": false,
    "Version": "1.0.0",
    "ModuleConfig": {
        "SystemID": "HANDSTACK",
        "BusinessServerUrl": "http://localhost:8421/transact/api/transaction/execute",
        "CircuitBreakResetSecond": 60,
        "IsValidationRequest": false,
        "IsAllowDynamicRequest": false,
        "AllowTenantTransactionCommands": [ "D", "G" ],
        "IsLogServer": true,
        "LogServerUrl": "http://localhost:8421/logger/api/log/insert",
QCN

transact 모듈 설정 확인하기 (3/6)

        "IsTransactionLogging": true,
        "IsTransactAggregate": true,
        "IsDataMasking": false,
        "MaskingChar": "*",
        "MaskingMethod": "Syn",
        "ContractBasePath": [
            "../contracts/transact"
        ],
        "AvailableEnvironment": [ "P", "D", "S" ],
        "IsCodeDataCache": true,
        "CodeDataCacheTimeout": 20,
        "ModuleBasePath": "../modules/transact",
        "TransactionAggregateBasePath": "../sqlite/HDS/transact/aggregate",
        "UseApiAuthorize": false,
QCN

transact 모듈 설정 확인하기 (4/6)

        "BypassAuthorizeIP": [
            "localhost",
            "127.0.0.1"
        ],
        "AllowRequestTransactions": {
            "HDS": [ "*" ]
        },
        "RoutingCommandUri": {
            "HDS|*|D|D": "http://localhost:8421/dbclient/api/query",
            "HDS|*|G|D": "http://localhost:8421/graphclient/api/query",
            "HDS|*|F|D": "http://localhost:8421/function/api/execution",
            "HDS|*|C|D": "http://localhost:8421/command/api/execution",
            "HDS|*|P|D": "http://localhost:8421/prompter/api/query",
            "HDS|*|D|P": "http://localhost:8421/dbclient/api/query",
QCN

transact 모듈 설정 확인하기 (5/6)

            "HDS|*|G|P": "http://localhost:8421/graphclient/api/query",
            "HDS|*|F|P": "http://localhost:8421/function/api/execution",
            "HDS|*|C|P": "http://localhost:8421/command/api/execution",
            "HDS|*|P|P": "http://localhost:8421/prompter/api/query",
            "HDS|*|D|T": "http://localhost:8421/dbclient/api/query",
            "HDS|*|G|T": "http://localhost:8421/graphclient/api/query",
            "HDS|*|F|T": "http://localhost:8421/function/api/execution",
            "HDS|*|C|T": "http://localhost:8421/command/api/execution",
            "HDS|*|P|T": "http://localhost:8421/prompter/api/query"
        },
        "EventAction": [],
        "SubscribeAction": [
            "transact.Events.TransactRequest"
        ],
QCN

transact 모듈 설정 확인하기 (6/6)

        "PublicTransactions": [
            {
                "ApplicationID": "HDS",
                "ProjectID": "*",
                "TransactionID": "*"
            }
        ]
    }
}
QCN

logger 모듈 설정 확인하기 (1/3)

logger 모듈 참고하기

QCN

logger 모듈 설정 확인하기 (2/3)

{
    "ModuleID": "logger",
    "Name": "logger",
    "IsBundledWithHost": false,
    "Version": "1.0.0",
    "ModuleConfig": {
        "SystemID": "HANDSTACK",
        "IsSQLiteCreateOnNotSettingRequest": true,
        "LogDeleteRepeatSecond": 43200,
        "ModuleBasePath": "../modules/logger",
        "BusinessServerUrl": "http://localhost:8421/transact/api/transaction/execute",
        "EventAction": [],
        "SubscribeAction": [],
        "DataSource": [
QCN

logger 모듈 설정 확인하기 (3/3)

            {
                "ApplicationID": "HDS",
                "TableName": "TransactLog",
                "DataProvider": "SQLite",
                "RemovePeriod": -30,
                "ConnectionString": "URI=file:../sqlite/HDS/logger/transact.db;Journal Mode=Off;BinaryGUID=False;DateTimeFormat=Ticks;Version=3;",
                "IsEncryption": "N"
            }
        ]
    }
}
QCN

wwwroot 모듈 설정 확인하기 (1/2)

wwwroot 모듈 참고하기

libman.json 으로 화면 개발에 필요한 오픈소스 관리합니다.

QCN

wwwroot 모듈 설정 확인하기 (2/2)

{
    "ModuleID": "wwwroot",
    "Name": "wwwroot",
    "IsBundledWithHost": false,
    "Version": "1.0.0",
    "ModuleConfig": {
        "SystemID": "HANDSTACK",
        "BusinessServerUrl": "http://localhost:8421/transact/api/transaction/execute",
        "ModuleLogFilePath": "../log/wwwroot/module.log",
        "ContractRequestPath": "view",
        "ContractBasePath": "../contracts/wwwroot",
        "WWWRootBasePath": "../modules/wwwroot/wwwroot"
    }
}
QCN

새로 추가된 기본 모듈 확인하기

현재 HandStack 기본 모듈에는 다음 확장 모듈도 포함됩니다.

모듈 역할
command 계약 기반 CLI 프로세스와 Web URL 호출 실행
graphclient Neo4j, Memgraph 기반 Cypher 실행
prompter LLM 프롬프트 계약 실행과 도구 호출 관리
forwarder Playwright 기반 세션 유지형 포워드 프록시

자세한 설정은 /docs/reference/api/modules/[모듈 ID] 문서를 참고하세요.

QCN

파일에서 실행까지

  • 요청을 받을 Controller와 화면을 렌더링할 Pages를 찾습니다.
  • 계약 경로와 LoadModules를 대조합니다.
  • 모듈별 설정은 필요한 키부터 읽고 전체 예제와 참조 문서로 확장합니다.
QCN

설정 예제의 키·계정·비밀번호는 교육용 값입니다. 운영에서는 환경별 비밀 값으로 교체하고 로그·소스에 노출하지 않습니다. 전체 JSON은 연속된 참고 페이지로 제공하며 발표에서는 변경할 필드부터 확인합니다. 발표: 첫 화면의 목표를 말한 뒤 핵심 개념과 예제로 진행합니다. 확인 질문 뒤에는 답할 시간을 주고, 마지막 완료 기준을 남겨 질문을 받습니다. 발표 구성 참고: MIT OpenCourseWare, Patrick Winston, How to Speak (2018), https://ocw.mit.edu/courses/res-tll-005-how-to-speak-january-iap-2018/pages/how-to-speak/

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

질문 후 잠시 기다립니다. 답이 없으면 앞에서 본 예제를 다시 가리킵니다. 확인할 답: 로드 모듈 목록과 실행 폴더의 설정·계약 경로를 확인합니다. 소스와 실행본이 다를 수 있습니다. 다음 주제로 넘어가기 전에 차이를 청중의 표현으로 한 번 확인합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

이어지는 코드 조각입니다. 앞뒤 페이지를 순서대로 읽으며 전체 예제의 일부임을 설명합니다.

질문을 받는 동안 이 확인 기준을 화면에 남깁니다. 청중이 자신의 업무에 적용할 다음 행동 하나를 고르게 합니다.